#include "HMAC.h"
#include <string>

HMAC::HMAC() {}
HMAC::~HMAC() {}

int HMAC::getTamanioFirma() {
    return 2 * (this->getTamanioDigest());
}

int HMAC::getTamanioDigest() {
    return sha1.DigestSize();
}

std::string HMAC::generarFirma(const std::string &mensaje,
                               const std::string &clave) {
    std::string firma;
    byte i_pad[TAMANIO_BLOQUE_PAD], o_pad[TAMANIO_BLOQUE_PAD];

    memset(o_pad, CONST_O_PAD, TAMANIO_BLOQUE_PAD); // 92d es igual a 5ch
    memset(i_pad, CONST_I_PAD, TAMANIO_BLOQUE_PAD); //54d es igual a 36h

    byte *key = (byte *) clave.c_str();
    byte *msg = (byte *) mensaje.c_str();

    // Bloque i_key_pad mas el mensaje concatenado.
    size_t max_size = TAMANIO_BLOQUE_PAD + strlen((char *) msg);
    byte *i_key_pad_message = new byte[max_size];
    memset(i_key_pad_message, 0, max_size);

    byte padded_key[TAMANIO_BLOQUE_PAD];
    memset(padded_key, 0, TAMANIO_BLOQUE_PAD);

    int key_len = strlen((char *) key);

    if (key_len > TAMANIO_BLOQUE_PAD)
        sha1.CalculateDigest(padded_key, key, key_len);
    else
        memcpy(padded_key, key, key_len);

    // Genera el i_key_pad y  o_key_pad (XOR)
    int i = 0;
    while (i < TAMANIO_BLOQUE_PAD) {
        i_pad[i] = i_pad[i] ^ padded_key[i];
        o_pad[i] = o_pad[i] ^ padded_key[i];
        i++;
    }

    // Armo la entrada para la 1er pasada por SHA1: i_key_pad|mensaje
    memcpy(i_key_pad_message, i_pad, TAMANIO_BLOQUE_PAD);
    memcpy(i_key_pad_message + TAMANIO_BLOQUE_PAD, msg, strlen((char *) msg));

    static byte sum1[DIGEST_SIZE];
    sha1.CalculateDigest(sum1, i_key_pad_message, max_size);

    // Armo la entrada para la 2da pasada por SHA1: o_key_pad|sum1
    static byte o_key_pad_sum1[TAMANIO_BLOQUE_PAD + DIGEST_SIZE];
    memcpy(o_key_pad_sum1, o_pad, TAMANIO_BLOQUE_PAD);
    memcpy(o_key_pad_sum1 + TAMANIO_BLOQUE_PAD, sum1, DIGEST_SIZE);

    static byte sum2[DIGEST_SIZE];
    sha1.CalculateDigest(sum2, o_key_pad_sum1, TAMANIO_BLOQUE_PAD+DIGEST_SIZE);

    /* Paso cada nibble de los 20 bytes de la salida de SHA1 a un string
        quedando de 40 bytes */
    char hex_ascii_byte[3];

    for (int i = 0; i < 20; i++) {
       snprintf(hex_ascii_byte, sizeof(hex_ascii_byte), "%02X", (byte)sum2[i]);
       firma.append(hex_ascii_byte);
    }

    delete[] i_key_pad_message;
    return firma;
}
